package com.seemsea.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;


public class AbstractGenericDAO<T extends IGenericEntity<ID>, ID extends Serializable>
		implements IGenericDAO<T, ID> {

	private Class<IGenericEntity<ID>> clazz;

	protected EntityManager entityManager;

	
	
	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}

	@SuppressWarnings("unchecked")
	public AbstractGenericDAO() {
		super();
		Type[] types = ((ParameterizedType) getClass().getGenericSuperclass())
				.getActualTypeArguments();
		if (types[0] instanceof ParameterizedType) {
			clazz = (Class<IGenericEntity<ID>>) ((ParameterizedType) types[0])
					.getRawType();
		} else {
			clazz = (Class<IGenericEntity<ID>>) types[0];
		}
	}

	public void delete(ID id) throws UnsupportedOperationException {
		entityManager.remove(this.load(id));
	}

	public void delete(ID... ids) throws UnsupportedOperationException {
		deleteAll(get(ids));
	}

	public void delete(T object) throws UnsupportedOperationException {
		entityManager.remove(object);
	}

	public void delete(T... objects) throws UnsupportedOperationException {
		deleteAll(Arrays.asList(objects));
	}

	public void deleteAll() {
		deleteAll(getAll());
	}

	private void deleteAll(final Collection<T> objects)
			throws UnsupportedOperationException {

		for (T object : objects) {
			entityManager.remove(object);
		}
	}

	public void flushAndClear() {
		entityManager.flush();
		entityManager.clear();
	}

	@SuppressWarnings("unchecked")
	public T get(ID id) {
		return (T) entityManager.find(clazz, id);
	}

	public List<T> get(ID... ids) {
		List<T> objects = new ArrayList<T>();
		for(ID id : ids){
			objects.add(load(id));
		}
		return objects;
	}

	@SuppressWarnings("unchecked")
	public List<T> getAll() {
		return entityManager.createQuery("from " + clazz.getName()).getResultList();
	}

	public T load(ID id) throws EntityNotFoundException {
		T entity = get(id);

		if (entity == null) {
			throw new EntityNotFoundException("Entity " + clazz
					+ " is not found");
		}
		return entity;
	}

	public void refresh(T entity) {
		entityManager.refresh(entity);
	}

	public void save(T object) {
		if (object.getCode() == null) {
			entityManager.persist(object);
		} else {
			entityManager.merge(object);
		}
	}

	public void save(T... objects) {
		for (T t : objects) {
			save(t);
		}
	}

}
